package de.ifi.lmu.snitzelhunt.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;
import de.ifi.lmu.snitzelhunt.util.responses.AsyncServer_Image_DeleteResponse;
import de.ifi.lmu.snitzelhunt.util.responses.AsyncServer_Image_DownloadResponse;
import de.ifi.lmu.snitzelhunt.util.responses.AsyncServer_Image_UploadResponse;
import de.ifi.lmu.snitzelhunt.util.responses.AsyncServer_Route_CreateResponse;
import de.ifi.lmu.snitzelhunt.util.responses.Server_Image_DeleteResponse;
import de.ifi.lmu.snitzelhunt.util.responses.Server_Image_DownloadResponse;
import de.ifi.lmu.snitzelhunt.util.responses.Server_Image_UploadResponse;

/**
 * Utilities for handling operations on the remote mySQL database via HTTP POST
 * requests for images
 * 
 * @author jaschkowitz
 * 
 */
public class ImageServerUtility {

	/**
	 * Method to upload a Image to the mySQL Server. An AsyncTask is used to
	 * start a new thread outside of the UI Thread.
	 * {@link AsyncServer_Route_CreateResponse} target is used for the caller to
	 * get the response from the Server
	 * 
	 * 
	 * @param r
	 * @param target
	 */
	public void uploadImage(String imagepath,
			AsyncServer_Image_UploadResponse target) {
		new UploadTask(target).execute(imagepath);
	}

	/**
	 * Method to download a Image to the mySQL Server. An AsyncTask is used to
	 * start a new thread outside of the UI Thread.
	 * {@link AsyncServer_Route_CreateResponse} target is used for the caller to
	 * get the response from the Server
	 * 
	 * 
	 * @param r
	 * @param target
	 */
	public void downloadImage(long id, AsyncServer_Image_DownloadResponse target) {
		new DownloadTask(target).execute(id);
	}

	/**
	 * Method to delete a Image on the mySQL Server. An AsyncTask is used to
	 * start a new thread outside of the UI Thread.
	 * {@link AsyncServer_Route_CreateResponse} target is used for the caller to
	 * get the response from the Server
	 * 
	 * 
	 * @param r
	 * @param target
	 */
	public void deleteImage(long id, AsyncServer_Image_DeleteResponse target) {
		new DeleteTask(target).execute(id);
	}

	/**
	 * AsyncTask to upload a Image to the remote mySQL Database.
	 * 
	 * @author jaschkowitz
	 * 
	 */
	private class UploadTask extends
			AsyncTask<String, Void, Server_Image_UploadResponse> {
		private AsyncServer_Image_UploadResponse target;

		private UploadTask(AsyncServer_Image_UploadResponse targetAct) {
			super();
			target = targetAct;
		}

		@Override
		protected Server_Image_UploadResponse doInBackground(String... filepath) {
			InputStream connIS = null;
			OutputStream connOS = null;
			HttpURLConnection conn = null;
			Server_Image_UploadResponse resp = new Server_Image_UploadResponse();

			try {
				// Setup for the Connection
				URL url = new URL("http://141.84.213.225:8080/MSP/uploadImage");
				conn = (HttpURLConnection) url.openConnection();
				conn.setReadTimeout(10000);
				conn.setConnectTimeout(15000);
				conn.setRequestMethod("POST");
				conn.setDoInput(true);
				conn.setDoOutput(true);

				// Connect
				conn.connect();

				// post Image to Server
				Bitmap bmp = decodeSampledBitmapfromFile(filepath[0], 200, 200);
				ByteArrayOutputStream bos = new ByteArrayOutputStream();
				bmp.compress(CompressFormat.JPEG, 70, bos);
				InputStream in = new ByteArrayInputStream(bos.toByteArray());

				connOS = conn.getOutputStream();
				BufferedOutputStream imgOutStream = new BufferedOutputStream(
						connOS);
				byte[] buffer = new byte[1024];
				int bytesRead = -1;

				while ((bytesRead = in.read(buffer)) != -1) {
					imgOutStream.write(buffer, 0, bytesRead);
				}

				in.close();
				imgOutStream.close();

				Log.i("snitzelhunt util", "POST upload Image to URL : " + url);
				Log.i("snitzelhunt util",
						"Response Code : " + conn.getResponseCode());

				if (conn.getResponseCode() == 200) {
					connIS = conn.getInputStream();
					String recv = ConnectionHelper.readResponse(connIS);
					resp.setImageID(GSonUtility.deserializeLong(recv));
				}
				resp.setHttpStatus(conn.getResponseCode());
				return resp;
			} catch (IOException e) {
				ConnectionHelper.handleIOException(e);
			} finally {
				ConnectionHelper.cleanConnection(connIS, connOS, conn);
			}
			resp.setHttpStatus(-1);
			return resp;
		}

		protected void onPostExecute(Server_Image_UploadResponse result) {
			target.processServer_Image_UploadResponse(result);
		}
	}

	/**
	 * AsyncTask to upload a Image to the remote mySQL Database.
	 * 
	 * @author jaschkowitz
	 * 
	 */
	private class DownloadTask extends
			AsyncTask<Long, Void, Server_Image_DownloadResponse> {
		private AsyncServer_Image_DownloadResponse target;

		private DownloadTask(AsyncServer_Image_DownloadResponse targetAct) {
			super();
			target = targetAct;
		}

		@Override
		protected Server_Image_DownloadResponse doInBackground(Long... id) {
			InputStream connIS = null;
			OutputStream connOS = null;
			HttpURLConnection conn = null;
			Server_Image_DownloadResponse resp = new Server_Image_DownloadResponse();

			try {
				// Setup for the Connection
				URL url = new URL(
						"http://141.84.213.225:8080/MSP/downloadImage");
				conn = ConnectionHelper.setupHttpConnection(url);

				// Connect
				conn.connect();

				// post imageID to Server
				String json = GSonUtility.serializeLong(id[0]);
				connOS = conn.getOutputStream();
				ConnectionHelper.writeString(connOS, json);
				Log.i("snitzelhunt util", "POST read Image to URL : " + url);
				Log.i("snitzelhunt util",
						"Response Code : " + conn.getResponseCode());

				// get Server Response
				if (conn.getResponseCode() == 200) { // succesful
					// read Server Response
					connIS = conn.getInputStream();
					BufferedInputStream imgBufferedStream = new BufferedInputStream(
							connIS);
					Bitmap bmp = BitmapFactory.decodeStream(imgBufferedStream);
					resp.setBmp(bmp);
					imgBufferedStream.close();
				}
				resp.setHttpStatus(conn.getResponseCode());
				return resp;
			} catch (IOException e) {
				ConnectionHelper.handleIOException(e);
			} finally {
				ConnectionHelper.cleanConnection(connIS, connOS, conn);
			}
			resp.setHttpStatus(-1);
			return resp;
		}

		protected void onPostExecute(Server_Image_DownloadResponse result) {
			target.processServer_Image_DownloadResponse(result);
		}
	}

	/**
	 * AsyncTask to delete a Image to the remote mySQL Database.
	 * 
	 * @author jaschkowitz
	 * 
	 */
	private class DeleteTask extends
			AsyncTask<Long, Void, Server_Image_DeleteResponse> {
		private AsyncServer_Image_DeleteResponse target;

		private DeleteTask(AsyncServer_Image_DeleteResponse targetAct) {
			super();
			target = targetAct;
		}

		@Override
		protected Server_Image_DeleteResponse doInBackground(Long... id) {
			InputStream connIS = null;
			OutputStream connOS = null;
			HttpURLConnection conn = null;
			Server_Image_DeleteResponse resp = new Server_Image_DeleteResponse();

			try {
				// Setup for the Connection
				URL url = new URL("http://141.84.213.225:8080/MSP/deleteImage");
				conn = ConnectionHelper.setupHttpConnection(url);

				// Connect
				conn.connect();

				// post imageID to Server
				String json = GSonUtility.serializeLong(id[0]);
				connOS = conn.getOutputStream();
				ConnectionHelper.writeString(connOS, json);
				Log.i("snitzelhunt util", "POST read Image to URL : " + url);
				Log.i("snitzelhunt util",
						"Response Code : " + conn.getResponseCode());

				resp.setHttpStatus(conn.getResponseCode());
				return resp;
			} catch (IOException e) {
				ConnectionHelper.handleIOException(e);
			} finally {
				ConnectionHelper.cleanConnection(connIS, connOS, conn);
			}
			resp.setHttpStatus(-1);
			return resp;
		}

		protected void onPostExecute(Server_Image_DeleteResponse result) {
			target.processServer_Image_DeleteResponse(result);
		}
	}

	/**
	 * Efficient loading of large images from a File. Analog to Android
	 * Training.
	 * 
	 * @param file
	 * @param reqWidth
	 * @param reqHeight
	 * @return
	 */
	public static Bitmap decodeSampledBitmapfromFile(String file, int reqWidth,
			int reqHeight) {

		// First decode with inJustDecodeBounds=true to check dimensions
		final BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;

		BitmapFactory.decodeFile(file, options);

		// Calculate inSampleSize
		options.inSampleSize = calculateInSampleSize(options, reqWidth,
				reqHeight);

		// Decode bitmap with inSampleSize set
		options.inJustDecodeBounds = false;
		return BitmapFactory.decodeFile(file, options);
	}

	/**
	 * Calculate needed sample size for UI. See Android Training.
	 * 
	 * @param options
	 * @param reqWidth
	 * @param reqHeight
	 * @return
	 */
	public static int calculateInSampleSize(BitmapFactory.Options options,
			int reqWidth, int reqHeight) {
		// Raw height and width of image
		final int height = options.outHeight;
		final int width = options.outWidth;
		int inSampleSize = 1;

		if (height > reqHeight || width > reqWidth) {

			final int halfHeight = height / 2;
			final int halfWidth = width / 2;

			// Calculate the largest inSampleSize value that is a power of 2 and
			// keeps both
			// height and width larger than the requested height and width.
			while ((halfHeight / inSampleSize) > reqHeight
					&& (halfWidth / inSampleSize) > reqWidth) {
				inSampleSize *= 2;
			}
		}
		return inSampleSize;
	}

}
